package com.pz.gulimall.order.service.impl;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.pz.gulimall.common.constant.MQConstant;
import com.pz.gulimall.common.constant.OrderStatusEnum;
import com.pz.gulimall.common.exception.BizCodeEnum;
import com.pz.gulimall.common.exception.RRException;
import com.pz.gulimall.common.to.*;
import com.pz.gulimall.common.to.mq.OrderReleaseTo;
import com.pz.gulimall.common.utils.PascalCamelCaseTransfer;
import com.pz.gulimall.common.utils.R;
import com.pz.gulimall.order.entity.MqMessageEntity;
import com.pz.gulimall.order.entity.OrderItemEntity;
import com.pz.gulimall.order.feign.ProductFeign;
import com.pz.gulimall.order.feign.WareFeign;
import com.pz.gulimall.order.service.MqMessageService;
import com.pz.gulimall.order.service.OrderItemService;
import com.pz.gulimall.order.vo.res.OrderInfoResVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pz.gulimall.common.utils.PageUtils;
import com.pz.gulimall.common.utils.Query;

import com.pz.gulimall.order.dao.OrderDao;
import com.pz.gulimall.order.entity.OrderEntity;
import com.pz.gulimall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.concurrent.ListenableFutureCallback;

import javax.annotation.Resource;

import static com.pz.gulimall.order.config.MQConfig.orderCreateRoutingKey;
import static com.pz.gulimall.order.config.MQConfig.orderEventExchangeName;


@Service("orderService")
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
    @Resource
    private OrderItemService orderItemService;
    @Resource
    private ProductFeign productFeign;
    @Resource
    private ThreadPoolExecutor executor;
    @Resource
    private WareFeign wareFeign;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private MqMessageService mqMessageService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<OrderEntity> queryWrapper = new QueryWrapper<>();
        String memberId = (String) params.get("memberId");
        String status = (String) params.get("status");
        String sort = (String) params.get("sort");
        String orderId = (String) params.get("orderId");
        if(!StringUtils.isEmpty(memberId)){
            queryWrapper.eq("member_id", memberId);
        }
        if(!StringUtils.isEmpty(status)){
            queryWrapper.and(i -> i.eq("status", status));
        }
        if(!StringUtils.isEmpty(orderId)) {
            queryWrapper.and(i -> i.eq("id", orderId));
        }
        if(!StringUtils.isEmpty(sort)){
            String[] str = sort.split("_");
            String order = str[1];
            String column = PascalCamelCaseTransfer.PascalToCamel(str[0]);
            if (order.equals("ASC")){
                queryWrapper.orderByAsc(column);
            } else {
                queryWrapper.orderByDesc(column);
            }
        }

        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                queryWrapper
        );

        PageUtils pageUtils = new PageUtils(page);
        List<OrderInfoResVo> list = new ArrayList<>();

       pageUtils.getList().forEach(item->{
           OrderInfoResVo orderInfoResVo = new OrderInfoResVo();
           BeanUtils.copyProperties(item, orderInfoResVo);
           List<OrderItemEntity> orderItems = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_id", orderInfoResVo.getId()));
           List<OrderInfoResVo.OrderItem> collect = orderItems.stream().map(i -> {
               OrderInfoResVo.OrderItem orderItem = new OrderInfoResVo.OrderItem();
               BeanUtils.copyProperties(i, orderItem);
               return orderItem;
           }).collect(Collectors.toList());
           orderInfoResVo.setOrderItems(collect);
           list.add(orderInfoResVo);
       });
       pageUtils.setList(list);

        return pageUtils;
    }

    @Transactional
    @Override
    public R saveOrder(OrderInfoTo orderInfo) {
        Map<Long, Integer> skuIdCountMap = new HashMap<>(); //{key: suId, value:skuQuantity}
        List<Long> skuIds = new ArrayList<>();
        OrderReleaseTo orderReleaseMsg = new OrderReleaseTo(); //发送给MQ的消息
        orderInfo.getOrderItems().forEach(item -> {
            skuIds.add(item.getSkuId());
            skuIdCountMap.put(item.getSkuId(), item.getCount());
        });

        OrderEntity order = new OrderEntity();
        BeanUtils.copyProperties(orderInfo, order);
        order.setCreateTime(new Date());
        //1. 查购物项的信息
        Map<Long, String> skuIdSaleAttrsMap = new HashMap<>(); //{key: skuId, value: saleAttrs(json格式)}
        //1.1 查询销售信息
        CompletableFuture<List<SkuSaleAttrTo>> task1 = CompletableFuture.supplyAsync(() -> {
            try {
                R ans = productFeign.getSaleAttrBatch(skuIds);
                if (ans.getCode() == 0) {
                    return ans.getDataArray(SkuSaleAttrTo.class);
                } else {
                    throw new RRException(ans);
                }
            } catch (Exception e) {
                throw new RRException(BizCodeEnum.AUTH_RPC_PRODUCT_FAILED, e.getMessage());
            }

        }, executor);
        CompletableFuture<Void> task2 = task1.thenAcceptAsync((res) -> res.forEach(item ->
                skuIdSaleAttrsMap.put(item.getSkuId(), JSON.toJSONString(item.getSaleAttrValues()))), executor);

        //1.2 查询sku基本信息
        CompletableFuture<List<SkuInfoResultMapTo>> task3 = CompletableFuture.supplyAsync(() -> {
            try {
                R ans = productFeign.skuInfo(skuIds);
                if (ans.getCode() == 0) {
                    return ans.getDataArray(SkuInfoResultMapTo.class);
                } else {
                    throw new RRException(ans.getMsg(), ans.getCode());
                }
            } catch (Exception e) {
                throw new RRException(BizCodeEnum.ORDER_RPC_PRODUCT, e.getMessage());
            }
        }, executor);

        CompletableFuture.allOf(task1, task2, task3);

        List<SkuInfoResultMapTo> skuInfos;
        try {
            skuInfos = task3.get();
        } catch (InterruptedException | ExecutionException e) {
            throw new RRException(BizCodeEnum.THREAD_EXCEPTION, e.getMessage());
        }

        //2. 订单数据处理
        //2.1 根据数据库重新计算支付金额 TODO 订单验价
        BigDecimal totalMoney = BigDecimal.valueOf(0);
        if(skuInfos != null){
            for (SkuInfoResultMapTo skuInfo : skuInfos) {
                totalMoney = totalMoney.add(skuInfo.getPrice().multiply(BigDecimal.valueOf(skuIdCountMap.get(skuInfo.getSkuId()))));
            }
            order.setTotalAmount(totalMoney);
        } else {
            throw new RRException(BizCodeEnum.ORDER_ITEMS_EMPTY);
        }

        //2.2 TODO 优惠和货运信息待填写

        // TODO 计算订单应付金额
        order.setPayAmount(totalMoney);

        order.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        //2.3 生成12位订单号
        String orderSn = UUID.randomUUID().toString().substring(0,12).toUpperCase();
        order.setOrderSn(orderSn);
        order.setConfirmStatus(0);
        order.setDeleteStatus(0);

        //2.4 先存订单基本信息
        this.save(order);
        BeanUtils.copyProperties(order, orderReleaseMsg);
        orderReleaseMsg.setOrderId(order.getId());

        //2.5 处理购物项数据
        List<OrderItemEntity> orderItemEntities = new ArrayList<>();
        for (SkuInfoResultMapTo skuInfo : skuInfos){
            OrderItemEntity orderItem = new OrderItemEntity();
            BeanUtils.copyProperties(skuInfo, orderItem);
            orderItem.setOrderId(order.getId());
            orderItem.setOrderSn(orderSn);
            orderItem.setCategoryId(skuInfo.getCatalogId());
            orderItem.setSkuPrice(skuInfo.getPrice());;
            orderItem.setSkuPic(skuInfo.getSkuDefaultImg());
            orderItem.setCount(skuIdCountMap.get(skuInfo.getSkuId()));
            orderItem.setSpuBrand(skuInfo.getBrandName());
            orderItem.setSkuAttrsVals(skuIdSaleAttrsMap.get(skuInfo.getSkuId()));
            //TODO 促销金额、积分、成长值
            orderItemEntities.add(orderItem);
        }

        //2.6 保存提交有货的订单购物项
        orderItemService.saveBatch(orderItemEntities);

        //3 锁定商品库存
        WareStockLockTo lockStockParam = new WareStockLockTo();
        lockStockParam.setOrderSn(orderSn);
        List<WareStockLockTo.OrderItemVo> orderItemVos = new ArrayList<>();
        skuIdCountMap.forEach((key, value) -> {
            WareStockLockTo.OrderItemVo temp = new WareStockLockTo.OrderItemVo();
            temp.setSkuId(key);
            temp.setCount(value);
            orderItemVos.add(temp);
        });
        lockStockParam.setOrderItems(orderItemVos);
        lockStockParam.setOrderId(order.getId());
        try {
            R ans = wareFeign.lockStock(lockStockParam);
            if (ans.getCode() == 0){
                // TODO 获取有货的仓库星系，配合后期运费计算
                List<LockStockResultTo> ansDataArray = ans.getDataArray(LockStockResultTo.class);
                // orderRelease入参，注入商品的库存信息
                List<OrderReleaseTo.OrderItemTo> orderItemTos = new ArrayList<>();
                ansDataArray.forEach(item -> {
                    OrderReleaseTo.OrderItemTo orderItemTo = new OrderReleaseTo.OrderItemTo();
                    orderItemTo.setSkuId(item.getSkuId());
                    List<OrderReleaseTo.ItemWareDetail> itemWareDetails = new ArrayList<>();
                    item.getWareStockMetas().forEach(stockMeta -> {
                        OrderReleaseTo.ItemWareDetail itemWareDetail = new OrderReleaseTo.ItemWareDetail();
                        BeanUtils.copyProperties(stockMeta, itemWareDetail);
                        itemWareDetails.add(itemWareDetail);
                    });
                    orderItemTo.setWareDetails(itemWareDetails);
                    orderItemTos.add(orderItemTo);
                });
                orderReleaseMsg.setOrderItems(orderItemTos);
            } else {
                // TODO 处理异常，接收库存不足的skuId集合
                log.info("库存异常-{}", ans);
                throw new RRException(ans);
            }
        } catch (Exception e) {
            throw new RRException(BizCodeEnum.ORDER_RPC_WARE_FAILED, e.getMessage());
        }

        //4 向mq发送订单创建完成的消息
        CorrelationData cd = new CorrelationData(UUID.randomUUID().toString().split("-")[0].toUpperCase()+System.currentTimeMillis());
        try {
            rabbitTemplate.convertAndSend(orderEventExchangeName, orderCreateRoutingKey, orderReleaseMsg, cd);
            cd.getFuture().addCallback(new ListenableFutureCallback<CorrelationData.Confirm>() {
                @Override
                public void onFailure(Throwable ex) {
                    // TODO 保存消息到数据库
                }

                @Override
                public void onSuccess(CorrelationData.Confirm result) {
                    if (!result.isAck()) {
                        // TODO 保存消息到数据库
                        log.info("消息ack异常-{}",result.getReason());
                    }
                }
            });
        } catch (Exception e) {
            log.error("发送异常, 没有抵达到mq服务器-{}", e.getMessage());
            // TODO 保存消息到数据库
            MqMessageEntity mqMessageEntity = new MqMessageEntity(cd.getId(),JSON.toJSONString(orderReleaseMsg),orderEventExchangeName,
                    orderCreateRoutingKey, orderReleaseMsg.getClass().getName(), MQConstant.MQStatus.REACH_FAILED.getCode(),
                    new Date(), null);
            mqMessageService.save(mqMessageEntity);
        }
        Map<String, Object> res = new HashMap<>();
        res.put("orderId", order.getId());
        res.put("orderSn", orderSn);

        return R.ok().put("data", res);
    }

    @Override
    public void updateOrderStatus(Long orderId, Integer status) {
        update(new UpdateWrapper<OrderEntity>().set("status", status).eq("id", orderId));
    }

    @Override
    public void cancelOrder(Long orderId) {
        // TODO
    }

    @Override
    public Integer checkOrderStatus(Long orderId) {
        return getById(orderId).getStatus();
    }


}